/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dspot.actions.processors;

import com.dspot.declex.annotation.ExportPopulate;
import com.dspot.declex.annotation.Model;
import com.dspot.declex.annotation.Populate;
import com.dspot.declex.api.action.process.ActionInfo;
import com.dspot.declex.api.action.process.ActionMethod;
import com.dspot.declex.api.action.process.ActionMethodParam;
import com.dspot.declex.api.action.processor.BaseActionProcessor;
import com.dspot.declex.api.util.FormatsUtils;
import com.helger.jcodemodel.IJExpression;
import com.helger.jcodemodel.JInvocation;
import com.helger.jcodemodel.JMethod;

import org.ohosannotations.annotations.EAbility;
import org.ohosannotations.annotations.EFraction;
import org.ohosannotations.annotations.export.Export;

import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;

import static com.helger.jcodemodel.JExpr.invoke;

/**
 * 负荷模型动作处理器
 *
 * @author Administrator
 * @date 2021/07/27
 */
public class LoadModelActionProcessor extends BaseActionProcessor {

    /**
     * 验证
     *
     * @param actionInfo 行动信息
     */
    @Override
    public void validate(ActionInfo actionInfo) {
        super.validate(actionInfo);

        ActionMethod init = getActionMethod("init");

        if (init.metaData != null) {
            ActionMethodParam initParam = init.params.get(0);
            Element field = (Element) initParam.metaData.get("field");

            if (field != null) {
                Model modelAnnotation = getAnnotation(field, Model.class);
                if (modelAnnotation == null) {
                    throw new IllegalStateException("The field " + field + " is not annotated with @Model");
                }

                actionInfo.isTimeConsuming = modelAnnotation.async();

                ActionMethod noPopulate = getActionMethod("noPopulate");
                if (noPopulate.metaData != null) {
                    Populate populatorAnnotation = getAnnotation(field, Populate.class);
                    ExportPopulate externalPopulatorAnnotation = getAnnotation(field, ExportPopulate.class);
                    if (populatorAnnotation == null && externalPopulatorAnnotation == null) {
                        throw new IllegalStateException("The field " + field + " is not annotated with @Populate");
                    }
                }
            }
        }
    }

    /**
     * 过程
     *
     * @param actionInfo 行动信息
     */
    @Override
    public void process(ActionInfo actionInfo) {
        super.process(actionInfo);

        ActionMethod init = getActionMethod("init");
        ActionMethod query = getActionMethod("query");
        ActionMethod orderBy = getActionMethod("orderBy");
        ActionMethod fields = getActionMethod("fields");

        if (init.metaData != null) {
            ActionMethodParam initParam = init.params.get(0);
            Element field = (Element) initParam.metaData.get("field");

            if (field != null) {

                Model modelAnnotation = getAnnotation(field, Model.class);

                if (field.getEnclosingElement().getAnnotation(EFraction.class) == null
                    && field.getEnclosingElement().getAnnotation(EAbility.class) == null
                    && getAnnotation(getElement(), Export.class) == null
                    && getAnnotation(getAnnotatedElement(), Export.class) == null)
                {
                    if (getActionMethod("keepCallingThread").metaData == null) {
                        addPreBuildBlock(getAction().invoke("keepCallingThread"));
                    }
                }

                actionInfo.isTimeConsuming = modelAnnotation.async();

                JMethod getModelMethod = getMethodInHolder(
                    "getLoadModelMethod", "com.dspot.declex.holder.ModelHolder", field
                );

                if (query.metaData == null) {
                    IJExpression queryExp = FormatsUtils.expressionFromString(modelAnnotation.query());
                    addPostInitBlock(getAction().invoke("query").arg(queryExp));
                }

                if (orderBy.metaData == null) {
                    IJExpression orderByExp = FormatsUtils.expressionFromString(modelAnnotation.orderBy());
                    addPostInitBlock(getAction().invoke("orderBy").arg(orderByExp));
                }

                if (fields.metaData == null) {
                    IJExpression fieldsExp = FormatsUtils.expressionFromString(modelAnnotation.fields());
                    addPostInitBlock(getAction().invoke("fields").arg(fieldsExp));
                }

                JInvocation invoke = invoke(getModelMethod);
                if (field.getModifiers().contains(Modifier.STATIC)) {
                    IJExpression context = getMethodInHolder("getContextRef");
                    invoke.arg(context);
                }
                invoke = invoke.arg(getAction().invoke("getArgs"))
                    .arg(getAction().invoke("getDone"))
                    .arg(getAction().invoke("getFailed"));

                addPostBuildBlock(invoke);
            }
        }
    }

}
